import {Injectable, Injector} from '@angular/core';
import {Router} from '@angular/router';
import {
    HttpErrorResponse,
    HttpEvent,
    HttpHandler,
    HttpInterceptor,
    HttpRequest, HttpResponse,
} from '@angular/common/http';
import {Observable, throwError} from 'rxjs';
import {catchError, debounceTime, delay, mergeMap, retryWhen, scan, tap, timeout} from 'rxjs/operators';

import {NotificationService} from '../services/notification.service';

const CODEMESSAGE = {
    '0': '请求超时，请检查网络是否断开或者链接是否正确',
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队（异步任务）。',
    204: '删除数据成功。',
    400: '发出的请求有错误，服务器没有进行新建或修改数据的操作。',
    401: '用户没有权限（令牌、用户名、密码错误）。',
    403: '用户得到授权，但是访问是被禁止的。',
    404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除，且不会再得到的。',
    422: '当创建一个对象时，发生一个验证错误。',
    500: '服务器发生错误，请检查服务器。',
    502: '网关错误。',
    503: '服务不可用，服务器暂时过载或维护。',
    504: '网关超时。',
};

/** 超时时间 */
const DEFAULTTIMEOUT = 5000;

/** 最大重试次数 */
const MAXRETRYCOUNT = 3;

/** 防止重复点击 网络重复请求 */
const MAXDEBOUNCETIME = 1000;


/**
 * 默认HTTP拦截器，其注册细节见 `app.module.ts`
 */
@Injectable()
export class DefaultInterceptor implements HttpInterceptor {
    constructor(private injector: Injector, private readonly notificationService: NotificationService) {
    }

    intercept(req: HttpRequest<any>, next: HttpHandler):Observable<HttpEvent<any>> {
        const time = new Date().getTime();

        const newReq = req.clone({
            setHeaders: {
                // REQUEST_FROM: "WEB_OFFICIAL",
                // TIMESTAMP: time.toString(),
            }
        });

        return next.handle(newReq).pipe(
            debounceTime(MAXDEBOUNCETIME),
            timeout(DEFAULTTIMEOUT),
            retryWhen(err$ => {
                // 重试
                return err$.pipe(
                    scan((errCount, err) => {
                        if (errCount >= MAXRETRYCOUNT) {
                            throw err;
                        }
                        return errCount + 1;
                    }, 0),
                    delay(1000),
                    tap(errCount => {
                        // 副作用
                        if (errCount === 1) {
                            // 第一次重试时，提示用户
                            this.notificationService.error('网络超时,正在重新请求中...');
                        }
                    })
                )
            }),
            catchError((err: HttpErrorResponse) => {
                this.notificationService.error('网络超时, 请重试');
                return throwError('网络超时' + err.name + err.message)
            }),
            mergeMap((event: any) => {
                if (event instanceof HttpResponse && event.status !== 200) {
                    return Observable.create(event);
                }

                return Observable.create((observer: { next: (arg0: any) => any; }) => observer.next(event)); // 请求成功返回响应

            }),
            catchError((err: HttpErrorResponse) => {
                switch (err.status) {
                    case 401:
                        this.notificationService.error('未登录或登录已过期，请重新登录。');
                        // 清空 token 信息
                        // (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).clear();
                        this.goTo('/pages/auth/login');
                        break;
                    case 403:
                        this.notificationService.error('服务器错误代码:403');
                        console.log(err);
                        break;
                    case 404:
                        this.goTo(`/pages/errors/error-404`);
                        break;
                    case 500:
                        this.goTo(`/pages/errors/error-500`);
                        console.log(err);
                        break;
                    case 502:
                        this.notificationService.error('服务器错误代码:502');
                        console.log(err);
                        break;
                    default:
                        console.warn(err);
                        break;
                }
                return throwError(err.status);
            })
        ) as any;
    }

    private goTo(url: string): void {
        setTimeout(() => this.injector.get(Router).navigateByUrl(url));
    }
}